package com.yuanss.smart.framework.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.net.www.protocol.jar.JarURLConnection;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


/**
 * @Description 类操作工具类
 * @Date 2024/11/14 下午2:05
 * @Created by 菜到不敢run
 */
public class ClassUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

    /**
     * 获取类加载器
     * 获取当前线程中的ClassLoader即可
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 加载类
     * 加载类需要提供类名与是否初始化标志，初始化是指是否执行类的静态代码块。
     * 为了提高加载类的性能，可将isInitialized参数设为false
     *
     * forName(String name, boolean initialize, ClassLoader loader)方法:
     * 第一个参数是类的全名;
     * 第二个参数是是否初始化类;
     * 第三个参数是加载时使用的类加载器;
     * 如果使用的方法是Class.forName(String name)时，它等价于initialize的值为true，loader的值为当前类的类加载器
     */
    public static Class<?> loadClass(String className, boolean isInitialized){
        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
        } catch (ClassNotFoundException e) {
            LOGGER.error("load class failure", e);
            throw new RuntimeException(e);
        }
        return cls;
    }

    /**
     * 获取指定包名下的所有类
     */
    public static Set<Class<?>> getClassSet(String packageName){
        Set<Class<?>> classSet = new HashSet<Class<?>>();
        try {
            //得到包名下的资源URL枚举
            //Enumeration枚举接口，用法和Iterator相似，提供了遍历Vector和HashTable类型集合元素的功能，不支持元素的移除操作
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                if (url == null){
                    continue;
                }
                String protocol = url.getProtocol();
                if ("file".equals(protocol)){
                    //获取文件路径时，里面的路径空格会被"%20"代替,因此要重新替换成空格
                    String packagePath = url.getPath().replaceAll("%20", " ");
                    addClass(classSet, packagePath, packageName);
                } else if ("jar".equals(protocol)){
                    JarURLConnection jarURLConnection = (JarURLConnection)url.openConnection();
                    if (jarURLConnection != null){
                        JarFile jarFile = jarURLConnection.getJarFile();
                        if (jarFile != null){
                            Enumeration<JarEntry> jarEntrys = jarFile.entries();
                            while (jarEntrys.hasMoreElements()){
                                JarEntry jarEntry = jarEntrys.nextElement();
                                String jarEntryName = jarEntry.getName();
                                if (jarEntryName.endsWith(".class")){
                                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/",".");
                                    doAddClass(classSet, className);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("get class set failure", e);
            throw new RuntimeException(e);
        }
        return classSet;
    }

    /**
     * 开始加载类
     */
    private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName){
        //把该包路径下的全部.class文件与文件夹加入到files数组里
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
            }
        });
        //遍历文件数组
        if(files == null){
            return;
        }
        for (File file : files){
            String fileName = file.getName();
            //如果是.class文件，则直接加载它
            if (file.isFile()){
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                if (StringUtil.isNotEmpty(packageName)){
                    className = packageName + "." + className;
                }
                doAddClass(classSet, className);
            }
            //如果是文件夹，则进行递归操作
            else {
                String subPackagePath = fileName;
                if (StringUtil.isNotEmpty(packagePath)){
                    subPackagePath = packagePath + "/" + subPackagePath;
                }
                String subPackageName = fileName;
                if (StringUtil.isNotEmpty(packageName)){
                    subPackageName = packageName + "." + subPackageName;
                }
                addClass(classSet, subPackagePath, subPackageName);
            }
        }
    }

    /**
     * 加载类(但不初始化)
     * 并且把类加入到Set中
     */
    private static void doAddClass(Set<Class<?>> classSet, String className){
        Class<?> cls = loadClass(className, false);
        classSet.add(cls);
    }
}
